package com.lancoo.ccas53.arrangecourse.clashesprocess;

import com.lancoo.ccas53.arrangecourse.common.BinaryUtil;
import com.lancoo.ccas53.arrangecourse.common.StrUtil;
import com.lancoo.ccas53.arrangecourse.entities.ClassHour;
import com.lancoo.ccas53.arrangecourse.entities.CourseUnit;
import com.lancoo.ccas53.arrangecourse.entities.TimeslotRoom;
import com.lancoo.ccas53.entity.ScheduleConflictInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description 记录冲突信息
 * @Author Fortysun
 * @Date 2023/6/6 15:18
 **/
@Slf4j
public class TimeslotRoomClashesRecord extends ClashesCompute  {
    /**
     * 计算教室容量冲突
     * @param classHourList 课时对象列表
     * @param classHourA 课时对象
     * @param conflictMap 冲突信息记录的map
     * @param roomIds
     * @return
     */
    public Map<Integer, List<ScheduleConflictInfo>> recordClashes(CopyOnWriteArrayList<ClassHour> classHourList,
                                                                  ClassHour classHourA,
                                                                  Map<Integer, List<ScheduleConflictInfo>> conflictMap,
                                                                  List<Long> roomIds) {

        for (ClassHour classHourB : classHourList) {
            //当前排课单元的，教学班单元信息；
            CourseUnit curCourseUnit = classHourA.getCourseUnit();
            //与当前排课单元，冲突的， 排课单元的教学班单元信息；
            CourseUnit clashCourseUnit = classHourB.getCourseUnit();

            //如果教室课时分组对象为空，说明此授课类型教学班没有分配教室课时分组，
            //那么就不存在冲突检测的问题
            if (classHourB.getTimeslotRoom() == null) {
                //直接跳过本次循环，进入下一次循环
                continue;
            }

            //不同的教学班的课程安排，存在周次交叉的情况
            if (!curCourseUnit.getUnitId().equals(clashCourseUnit.getUnitId())
                    && isWeekClash(curCourseUnit.getClassWeeks(),
                    clashCourseUnit.getClassWeeks())) {
                //记录教室冲突
                conflictMap = recordRoomClashes(classHourA, classHourB, conflictMap, roomIds);

                //记录教师冲突
                conflictMap = recordTeacherClashes(classHourA, classHourB, conflictMap);

                //记录行政班冲突
                conflictMap = recordPhyClassClashes(classHourA, classHourB, conflictMap);

                //当教学班不是在线选课类型的时候，才需要记录学生安排冲突
                if (curCourseUnit.getTeachingClassType() != 1) {
                    //记录学生冲突
                    conflictMap = recordStudentClashes(classHourA, classHourB, conflictMap);
                }
            }
        }

        return conflictMap;
    }


    /**
     * 添加教室冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param conflictMap 冲突信息记录的map
     * @param roomIds
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> recordRoomClashes(ClassHour classHourA,
                                                                       ClassHour classHourB,
                                                                       Map<Integer, List<ScheduleConflictInfo>> conflictMap,
                                                                       List<Long> roomIds) {
        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();

        //不同的授课类型教学班，存在周次交叉的情况，并且对应的教室课时id相同，则教室、课时安排冲突
        if (curTimeslotRoom.getRoomId().equals(clashTimeslotRoom.getRoomId())
            && !roomIds.contains(curTimeslotRoom.getRoomId())) {
            //将classHourA的教室冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourA, classHourB, conflictMap, 1,-1L);

            //B与A互相冲突，将classHourB的教室冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourB, classHourA, conflictMap, 1,-1L);
        }

        return conflictMap;
    }

    /**
     * 添加学生冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param conflictMap 冲突信息记录的map
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> recordStudentClashes(ClassHour classHourA,
                                                            ClassHour classHourB,
                                                          Map<Integer, List<ScheduleConflictInfo>> conflictMap) {

        //计算冲突的学生的id
        String  clashesStudentId = StrUtil.clashString(classHourA.getCourseUnit().getStudentIds(),
                    classHourB.getCourseUnit().getStudentIds());

        //相同的课时，安排了相同的学生，那么学生冲突
        if (StringUtils.isNotBlank(clashesStudentId)) {
            Long clashUnitId = 0L;
            try {
                clashUnitId = Long.valueOf(clashesStudentId);
            } catch (Exception ex) {
                log.error("#TimeslotRoomClashesRecord.recordStudentClashes# -- 冲突学生id转换失败~");
            }
            //将classHourA的学生冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourA, classHourB, conflictMap, 4,clashUnitId);

            //B与A互相冲突，将classHourA的学生冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourB, classHourA, conflictMap, 4,clashUnitId);
        }

        return conflictMap;
    }

    /**
     * 添加行政班冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param conflictMap 冲突信息记录的map
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> recordPhyClassClashes(ClassHour classHourA,
                                                                          ClassHour classHourB,
                                                                          Map<Integer, List<ScheduleConflictInfo>> conflictMap) {
        //获取A的行政班id
        String phyClassAId = classHourA.getCourseUnit().getPhyClassId();
        //获取B的行政班id
        String phyClassBId = classHourB.getCourseUnit().getPhyClassId();

        //分组 拆班，不进行行政班检测
        if (!StringUtils.isEmpty(classHourA.getCourseUnit().getUniqueShiftSign())
                || !StringUtils.isEmpty(classHourA.getCourseUnit().getSplitClassUuid())){
            phyClassAId = "";
        }

        //行政班相同，说明时行政班冲突
        if (StrUtil.isStrClashes(phyClassAId, phyClassBId)) {
            //将classHourA的行政班冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourA, classHourB, conflictMap, 6,-1L);

            //B与A互相冲突，将classHourA的行政班冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourB, classHourA, conflictMap, 6,-1L);
        }

        return conflictMap;
    }


    /**
     * 添加教师冲突
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param conflictMap 冲突信息记录的map
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> recordTeacherClashes(ClassHour classHourA,
                                                            ClassHour classHourB,
                                                          Map<Integer, List<ScheduleConflictInfo>> conflictMap) {

        //如果有一个教学班排课单元没有设置教师信息,不计算教师冲突
        if (StringUtils.isBlank(classHourA.getCourseUnit().getTeacherIds())
                || StringUtils.isBlank(classHourB.getCourseUnit().getTeacherIds())) {
            return conflictMap;
        }

        //计算冲突的教师的id
        String clashesTeacherId = StrUtil.clashString(classHourA.getCourseUnit().getTeacherIds(),
                classHourB.getCourseUnit().getTeacherIds());

        //相同的课时，安排了相同的教师，那么教师冲突
        if (StringUtils.isNotBlank(clashesTeacherId)) {
            Long clashUnitId = 0L;
            try {
                clashUnitId = Long.valueOf(clashesTeacherId);
            } catch (Exception ex) {
                log.error("#TimeslotRoomClashesRecord.recordTeacherClashes# -- 冲突教师id转换失败~");
            }
            //将classHourA的教师冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourA, classHourB, conflictMap, 3,clashUnitId);

            //B与A互相冲突，将classHourA的教师冲突记录到冲突列表
            conflictMap = addRecordToMap(classHourB, classHourA, conflictMap, 3,clashUnitId);

            return conflictMap;
        }
        return conflictMap;
    }

    /**
     * 将冲突记录到map
     * @param classHourA 排课单元
     * @param classHourB 排课单元
     * @param conflictMap 冲突信息记录的map
     * @param clashType 冲突类型
     * @param clashUnitId 学生冲突，冲突的学生id
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> addRecordToMap(ClassHour classHourA,
                                                                    ClassHour classHourB,
                                                                    Map<Integer, List<ScheduleConflictInfo>> conflictMap,
                                                                    Integer clashType,
                                                                    Long clashUnitId) {
        //当前排课单元的，教学班单元信息；
        CourseUnit curCourseUnit = classHourA.getCourseUnit();
        //与当前排课单元，冲突的， 排课单元的教学班单元信息；
        CourseUnit clashCourseUnit = classHourB.getCourseUnit();
        //获取A课表记录的教室课时分组
        TimeslotRoom curTimeslotRoom = classHourA.getTimeslotRoom();
        //获取B课表记录的教室课时分组
        TimeslotRoom clashTimeslotRoom = classHourB.getTimeslotRoom();
        //计算发生冲突的周次字符串
        String clashWeeks = BinaryUtil.binaryAnd(curTimeslotRoom.getWeeks(), clashTimeslotRoom.getWeeks());
        //生成冲突信息
        ScheduleConflictInfo conflictInfo = getConflictInfo(curCourseUnit,
                curTimeslotRoom,
                clashCourseUnit,
                clashWeeks,
                clashType,
                clashUnitId);
        //将冲突信息加入map
        conflictMap = addConflictInfoToMap(curCourseUnit.getUnitId(), conflictInfo, conflictMap);

        return conflictMap;
    }


    /**
     * 将冲突信息，添加到map
     * @param courseUintId 排课单元，对应的教学班课程的id
     * @param conflictInfo 冲突信息对象
     * @param conflictMap 冲突信息记录的map
     * @return
     */
    private Map<Integer, List<ScheduleConflictInfo>> addConflictInfoToMap(
            Integer courseUintId,
            ScheduleConflictInfo conflictInfo,
            Map<Integer, List<ScheduleConflictInfo>> conflictMap) {

        //将获取到的classHour存入map
        if (conflictMap.get(courseUintId) == null) {
            //创建排课单元列表信息
            List<ScheduleConflictInfo> conflictList = new ArrayList<>();
            //将生成的教学班排课单元，添加到列表
            conflictList.add(conflictInfo);
            //将列表添加到map中
            conflictMap.put(courseUintId, conflictList);
        }
        else {
            //直接将排课单元信息，添加到map里指定的列表中
            conflictMap.get(courseUintId).add(conflictInfo);
        }

        return conflictMap;
    }

    /**
     * 获取冲突对象
     * @param curCourseUnit 当前排课单元，对应的教学班信息
     * @param curTimeslotRoom 当前排课单元，分配的教室课时分组信息
     * @param clashCourseUnit 与当前排课单元，冲突的排课单元对应的教学班信息；
     * @param clashWeeks 冲突的周次信息
     * @param clashType 冲突类型
     * @param clashUnitId 学生冲突，冲突的学生id
     * @return
     */
    private ScheduleConflictInfo getConflictInfo(CourseUnit curCourseUnit,
                                                  TimeslotRoom curTimeslotRoom,
                                                  CourseUnit clashCourseUnit,
                                                  String clashWeeks,
                                                  Integer clashType,
                                                  Long clashUnitId) {

        //记录教师冲突，同一时间，安排了同一个老师
        ScheduleConflictInfo conflictInfo = initConflictInfo(curCourseUnit.getTaskId(),
                clashType,
                curCourseUnit.getTeachingClassId(),
                curTimeslotRoom.getRoomId(),
                curTimeslotRoom.getTimeslotCode(),
                clashCourseUnit.getTeachingClassId(),
                clashWeeks,
                clashCourseUnit.getUnitId(),
                clashUnitId,
                curCourseUnit.getTeachingType(),
                clashCourseUnit.getTeachingType(),
                curCourseUnit.getTimeGroup(),
                clashCourseUnit.getTimeGroup()
                );

        switch (clashType) {
            case 1://教室冲突
                conflictInfo.setConflictReason("教室安排冲突--当前教室："+curTimeslotRoom.getRoomName()
                        +"，在" + curTimeslotRoom.getTimeslotName()
                        +"同时安排了学期计划id为：" +curCourseUnit.getTaskId() + "的教学班："+ curCourseUnit.getTeachingClassName()
                        + "和学期计划id为："+clashCourseUnit.getTaskId()+ "的教学班："+ clashCourseUnit.getTeachingClassName());
                break;

            case 3://教师冲突
                conflictInfo.setConflictReason("教师安排冲突--当前学期计划id为：" +curCourseUnit.getTaskId()
                        + "的教学班："+ curCourseUnit.getTeachingClassName()
                        + "与学期计划id为："+clashCourseUnit.getTaskId()
                        + "的教学班："+ clashCourseUnit.getTeachingClassName()
                        + "，上课时间都是：" + curTimeslotRoom.getTimeslotName()
                        + "，但是两个班级都包括教师：");//以前传的是id，现在直接由数据库端接口层，直接生成名称，拼接在后面
                break;

            case 4://学生冲突
                conflictInfo.setConflictReason("学生安排冲突--当前学期计划id为：" +curCourseUnit.getTaskId()
                        + "的教学班："+ curCourseUnit.getTeachingClassName()
                        + "与学期计划id为："+clashCourseUnit.getTaskId()
                        + "的教学班："+ clashCourseUnit.getTeachingClassName()
                        + "，上课时间都是：" + curTimeslotRoom.getTimeslotName()
                        + "，但是两个班级都包括学生：");
                break;

            case 6://行政班冲突
                conflictInfo.setConflictReason("行政班安排冲突--当前学期计划id为：" +curCourseUnit.getTaskId()
                        + "的教学班："+ curCourseUnit.getTeachingClassName()
                        + "与学期计划id为："+clashCourseUnit.getTaskId()
                        + "的教学班："+ clashCourseUnit.getTeachingClassName()
                        + "，上课时间都是：" + curTimeslotRoom.getTimeslotName()
                        + "，但是两个班级都包括相同的行政班，行政班id为：" + curCourseUnit.getPhyClassId());
                break;

            default:
                break;
        }

        return conflictInfo;
    }

    /**
     * 初始化冲突对象
     * @param taskId 排课选课计划id
     * @param conflictType 冲突类型，1:教学班冲突 2教室冲突（容量不足） 3教师冲突 4学生冲突 5未分配（没有分配教室课时）
     * @param teachingClassId 当前排课单元对应的，教学班id
     * @param roomId 当前排课单元分配的教室id
     * @param timeslotCode 当前排课单元分配的课时编码
     * @param clashTeachingClassId 与当前排课单元冲突的，教学班的id
     * @param clashWeeks 冲突的周次
     * @param clashCourseUnitId 与当前教学班排课单元，产生冲突的教学班的排课单元id
     * @param clashUnitId 冲突的教师/学生id
     * @return
     */
    private ScheduleConflictInfo initConflictInfo(Integer taskId,
                                                 Integer conflictType,
                                                 Long teachingClassId,
                                                 Long roomId,
                                                 String timeslotCode,
                                                 Long clashTeachingClassId,
                                                 String clashWeeks,
                                                  Integer clashCourseUnitId,
                                                  Long clashUnitId,
                                                  Integer hourType,
                                                  Integer hourType2,
                                                  String timeGroup,
                                                  String clashTimeGroup) {
        ScheduleConflictInfo scheduleConflictInfo = new ScheduleConflictInfo();
        scheduleConflictInfo.setTaskId(taskId);
        scheduleConflictInfo.setConflictType(conflictType);
        scheduleConflictInfo.setTeachingClassId(teachingClassId);
        scheduleConflictInfo.setRoomId(roomId);
        scheduleConflictInfo.setTimeCode(timeslotCode);
        scheduleConflictInfo.setClashTeachingClassId(clashTeachingClassId);
        scheduleConflictInfo.setClashWeeks(clashWeeks);

        scheduleConflictInfo.setClashCourseUnitId(clashCourseUnitId);
        scheduleConflictInfo.setClashUnit(clashUnitId);

        scheduleConflictInfo.setHourType(hourType);
        scheduleConflictInfo.setClashHourType(hourType2);
        scheduleConflictInfo.setTimeGroup(timeGroup);
        scheduleConflictInfo.setClashTimeGroup(clashTimeGroup);
        return scheduleConflictInfo;
    }
}
